﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Text;
using HoldemGame;

namespace HoldemView
{

    public class ToolStripDirector
    {
        List<ToolStrip> _list = new List<ToolStrip>();

        public IList<ToolStrip> ToolStrips { get { return _list; } }
        public bool ThrowIfNone = true;
        public static int AddItemClick(ToolStripItemCollection ts, string id, EventHandler handler)
        {
            int count = 0;
            Regex re = new Regex(id);
            foreach (ToolStripItem item in ts)
            {
                if (re.Match(item.Name).Success)
                {
                    item.Click += new EventHandler(handler);
                    count++;
                }
                ToolStripMenuItem tsmi = item as ToolStripMenuItem;
                if (null != tsmi)
                    if (null != tsmi.DropDownItems)
                        count+=AddItemClick(tsmi.DropDownItems, id, handler);
            }

            return count;
        }

        public static int SetTag(ToolStripItem item, object tag, bool recursive)
        {
            item.Tag = tag;
            int count = 1;
            if (recursive)
            {
                ToolStripDropDownItem dd = item as ToolStripDropDownItem;
                foreach (ToolStripItem sub in dd.DropDownItems)
                    count+=SetTag(sub, tag, recursive);
            }
            return count;
        }
        public static int AddItemCheckedChanged(ToolStripItemCollection ts, string id, EventHandler handler)
        {
            int count = 0;
            Regex re = new Regex(id);
            foreach (ToolStripItem item in ts)
            {
                ToolStripButton button = item as ToolStripButton;
                if (null != button && re.Match(item.Name).Success)
                {
                    button.CheckedChanged += new EventHandler(handler);
                    count++;
                }
                ToolStripMenuItem tsmi = item as ToolStripMenuItem;
                if (null != tsmi)
                    if (null != tsmi.DropDownItems)
                        count += AddItemCheckedChanged(tsmi.DropDownItems, id, handler);
            }

            return count;
        }

        public static int EnableItem(ToolStripItemCollection ts, string id, bool enable)
        {
            int count = 0;
            Regex re = new Regex(id);
            foreach (ToolStripItem item in ts)
            {
                if (re.Match(item.Name).Success)
                {
                    item.Enabled = enable;
                    count++;
                }
                ToolStripMenuItem tsmi = item as ToolStripMenuItem;
                if (null != tsmi)
                    if (null != tsmi.DropDownItems)
                        count += EnableItem(tsmi.DropDownItems, id, enable);
            }

            return count;
        }

        public static int CheckItem(ToolStripItemCollection ts, string id, bool enable)
        {
            int count = 0;
            Regex re = new Regex(id);
            foreach (ToolStripItem item in ts)
            {
                ToolStripButton button = item as ToolStripButton;
                ToolStripMenuItem mnu = item as ToolStripMenuItem;
                if (re.Match(item.Name).Success)
                    if(null!=button)
                    {
                        button.Checked = enable;
                        count++;
                    }
                    else if (null != mnu)
                    {
                        mnu.Checked = enable;
                        count++;
                    }
                ToolStripMenuItem tsmi = item as ToolStripMenuItem;
                if (null != tsmi)
                    if (null != tsmi.DropDownItems)
                        count += CheckItem(tsmi.DropDownItems, id, enable);
            }

            return count;

        }


        public static List<ToolStripItem> FindItemsExact(ToolStrip ts, string name)
        {
            ToolStripItem[] items = ts.Items.Find(name, true);
            return new List<ToolStripItem>(items);
        }

        public static ToolStripItem GetItemExact(ToolStrip ts, string name)
        {
            ToolStripItem[] items = ts.Items.Find(name, true);
            return items.Length > 0 ? items[0] : null;
        }

        public ToolStripItem GetItemExact(string name)
        {
            foreach (ToolStrip ts in _list)
            {
                ToolStripItem item = GetItemExact(ts, name);
                if (null != item)
                    return item;
            }
            return null;
        }
        public List<ToolStripItem> FindItems(string name)
        {
            List<ToolStripItem> items = new List<ToolStripItem>();

            foreach (ToolStrip ts in this._list)
            {
                items.AddRange(ts.Items.Find(name, true));
            }
            return items;
        }

        public int EnableItem(string id, bool enable)
        {
            ToolStripItem item = null;
            int count = 0;
            foreach (ToolStrip ts in _list)
            {
                count += EnableItem(ts.Items, id, enable);
            }
            Check(count, id);
            return count;
        }

        public int CheckItem(string id, bool enable)
        {
            ToolStripItem item = null;
            int count = 0;
            foreach (ToolStrip ts in _list)
            {
                count += CheckItem(ts.Items, id, enable);
            }
            Check(count, id);
            return count;
        }

        public int AddItemClick(string id, EventHandler handler)
        {
            ToolStripItem item = null;
            int count = 0;
            foreach (ToolStrip ts in _list)
            {
                count += AddItemClick(ts.Items, id, handler);
            }
            Check(count, id);
            return count;
        }
        
        public int AddItemCheckedChanged(string id, EventHandler handler)
        {
            ToolStripItem item = null;
            int count = 0;
            foreach (ToolStrip ts in _list)
            {
                count += AddItemCheckedChanged(ts.Items, id, handler);
            }
            Check(count, id);
            return count;
        }

        void Check(int cnt, string id)
        {
            if (cnt < 1)
                if (ThrowIfNone)
                    throw new InvalidOperationException("Failed to find "+id+" toolstrip item");
        }

    }

 

    public class HoldemTableDirector : GameBase
    {
        private HoldemTable _table;

        private bool _isRunning;
        private bool _gameDirty = false;

        private Timer _timer;
        private int _runCount;
        private ToolStrip _tsDebug;
        private MenuStrip _msMain;

        private long _startTime;
        private ToolStripComboBox _cbRunCount;
        private bool _stepMode = true;
        private int _commitCount = 0;

        private ToolStripDirector _tsDirector = new ToolStripDirector();

        public HoldemTable Table { get { return _table; } set { _table=value; } }

        public ToolStrip DebugStrip { get { return _tsDebug; } set { _tsDebug = value; ToolStrips.ToolStrips.Add(_tsDebug); } }
        public MenuStrip MainMenuStrip { get { return _msMain; } set { _msMain = value; ToolStrips.ToolStrips.Add(_msMain); } }

        public ToolStripDirector ToolStrips { get { return _tsDirector; } set { _tsDirector = value; } }

        public ConnectionDirector ConnDirector;

        public HoldemTableDirector(HoldemTable table, IGame game) 
        {
            Table = table;
            Table.Director = this;

            this.ConnectGame(game);
            ConnDirector = new ConnectionDirector(game);

            _timer = new Timer();
            _timer.Tick += new EventHandler(Timer_Tick);
            _timer.Interval = 1000;
            _timer.Start();
        }

        GamePlayer ManualPlayer
        {
            get { return IsManualStep() ? this.State.Players.Current : null; }
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            UpdateToolStrips();
            _table.UpdateAction(ManualPlayer);
            if (_gameDirty)
            {
                UpdateAll();

            }
        }
        
        void UpdateAll()
        {
            if (State.Players.Count != _table.PlayerPanels.Count)
                _table.UpdateAll();
            else
            {
                _table.UpdatePlayers();
                //_table.UpdateCurrent();
                _table.UpdateBoard();
                _table.UpdateAction(ManualPlayer);
            }
            _gameDirty = false;
        }

        
        public void SubscribeToolstrips()
        {
            {

                ToolStrips.AddItemClick("...Next", Next_Clicked);

                ToolStrips.AddItemClick("...Continue", Continue_Clicked);

                ToolStrips.AddItemClick("...Pause", Pause_Clicked);

                ToolStrips.AddItemClick("...Reset", Reset_Clicked);

                ToolStripButton tsb = ToolStrips.GetItemExact("tsbShowAll") as ToolStripButton;
                
                if(null!=tsb)
                    tsb.CheckedChanged += new EventHandler(ShowAll_Checked_Changed);

                _cbRunCount = ToolStripDirector.GetItemExact(DebugStrip, "tsbRunCount") as ToolStripComboBox;


                ToolStrips.AddItemClick("...BankrollChart", BankRollChart_Clicked);

                ToolStrips.AddItemClick("...Commit", Commit_Clicked);

                ToolStrips.AddItemClick("...Connect", Connect_Clicked);

                UpdateToolStrips();
            }
        }

        public bool PocketCards(GamePlayer player, CardSet cs)
        {
            if (VerifyCards(player, cs, new List<Exception>())>0)
                return false;
            Server.PocketCards(player, cs);
            return true;
        }

        public bool BoardCards(CardSet cs)
        {
            if (VerifyBoardCards(cs, new List<Exception>())>0)
                return false;
            Server.BoardCards(cs);

            return true;
        }

        public int VerifyDups(CardSet cs, string suffix, List<Exception> errs)
        {
            List<Card> duplicates = new List<Card>();
            int dups = cs.UpdateMask(duplicates);
            if (dups > 0)
            {
                errs.Add(new InvalidOperationException(suffix+string.Format("There are {0} duplicates: {1}...", dups, duplicates[0])));
                return 1;
            }
            return 0;
        }

        public int VerifyCards(GamePlayer player, CardSet cs, List<Exception> errs)
        {
            int count = VerifyDups(cs, string.Format("{0}: ", player),errs);

            foreach (Card card in cs)
            {
                if (!card.IsValid)
                {
                    count++;
                    errs.Add(new InvalidOperationException(string.Format("{1}: Card {0} is invalid", card, player)));
                    continue;
                }
                int who = State.Table.FindCard(card);
                if (who >= 0)
                {
                    if (who != State.Players.IndexOf(player))
                    {
                        count++;
                        errs.Add(new InvalidOperationException(string.Format("{2}: Card {0} is owned by {1}", card, State.Players[who],player)));
                    }
                }
                else if (who == GameObject.BOARD)
                {
                    count++;
                    errs.Add(new InvalidOperationException(string.Format("{1}: Card {0} is board-card", card,player)));
                }
                else if (who != GameObject.INVISIBLE)
                {
                    count++;
                    errs.Add(new InvalidOperationException(string.Format("Wrong owner {0}", who)));
                }
            }

            return count;
        }

        public int VerifyBoardCards(CardSet cs, List<Exception> errs)
        {
            int count = VerifyDups(cs, "Board: ", errs);

            foreach (Card card in cs)
            {
                if (!card.IsValid)
                {
                    count++;
                    errs.Add(new InvalidOperationException(string.Format("Board: Card {0} is invalid", card)));
                    continue;
                }
                int who = State.Table.FindCard(card);
                if (who >= 0)
                {
                    count++;
                    errs.Add(new InvalidOperationException(string.Format("Board: Card {0} is owned by {1}", card, State.Players[who])));
                }else if (who == GameObject.INVISIBLE)
                        continue;
                else if(who==GameObject.BOARD)
                        continue;
                throw new InvalidOperationException(string.Format("Wrong owner {0}", who));
            }
            return count;
        }
        
        public void SubscribePlayerMenu(ToolStripMenuItem menu)
        {
  
            ToolStripDirector.AddItemClick(menu.DropDownItems, "...SetFirst",
                new EventHandler(SetFirst_Player_Clicked));
            ToolStripDirector.AddItemClick(menu.DropDownItems, "...SetCurrent",
                new EventHandler(SetCurrent_Player_Clicked));
            ToolStripDirector.AddItemClick(menu.DropDownItems, "...Manual",
                new EventHandler(Manual_Player_Clicked));
            
        }

        public bool CanCall()
        {
            return IsManualStep() && !IsRunning() && !IsBlind();
        }

        public bool CanRaise()
        {
            return IsManualStep() && !IsRunning() && State.CanRaise(State.Players.Current) && !IsBlind();
        }

        public bool CanFold()
        {
            return IsManualStep()&& !IsRunning() && !IsBlind();
        }

        public bool IsBlind()
        {
            //return IsManualStep() && !IsRunning() && State.GetBlindAmount(State.Players.Current) > 0;
            return false;
        }

        public void Raise()
        {
            if (!CanRaise())
                return;
            Server.Raise();
            if(!_stepMode)
                Continue();
        }

        public void Call()
        {
            if (!CanCall())
                return;
            Server.Call();
            if(!_stepMode)
                Continue();
        }

        public void Fold()
        {
            if (!CanFold())
                return;
            Server.Fold();
            if(!_stepMode)
                Continue();
        }

        public void Blind()
        {

            if (!IsBlind())
                return;
            throw new NotSupportedException();
            Server.Blind();
            if (!_stepMode)
                Continue();
        }

        public void UpdatePlayerMenu(ToolStripMenuItem menu, PlayerPanel pp)
        {
            ToolStripDirector.CheckItem(menu.DropDownItems, "...Manual", pp.Player.State.Get(PlayerState.ManualMode));
        }

        private PlayerPanel GetPlayerPanel(object sender)
        {
            ToolStripMenuItem item = sender as ToolStripMenuItem;
            PlayerPanel pp = item.Tag as PlayerPanel;
            if (null == pp)
                throw new InvalidOperationException("Command received from null PlayerPanel");
            return pp;
        }


        void Connect_Clicked(object sender, EventArgs e)
        {
            ConnDirector.Connect();
        }

        void Manual_Player_Clicked(object sender, EventArgs e)
        {
            PlayerPanel pp = GetPlayerPanel(sender);
            pp.Player.IsManual = !pp.Player.IsManual;
            UpdatePlayerMenu(pp.Menu, pp);
        }

        void SetFirst_Player_Clicked(object sender, EventArgs e)
        {
            PlayerPanel pp = GetPlayerPanel(sender);
            this.Server.SetFirst(pp.Player);
        }

        void SetCurrent_Player_Clicked(object sender, EventArgs e)
        {
            PlayerPanel pp = GetPlayerPanel(sender);
            this.Server.SetCurrent(pp.Player);
        }

        void BankRollChart_Clicked(object sender, EventArgs e)
        {

        }
        
        public int CommitCount { get { return _commitCount; } set { _commitCount= value; } }

        void Commit_Clicked(object sender, EventArgs e)
        {
            if (CommitCount > 0)
            {
                try
                {
                    Commit();
                }
                catch (InvalidOperationException ex)
                {
                    MessageBox.Show(ex.ToString());
                }
                //CommitCount = 0;
            }
        }

        void Commit()
        {
            if(CommitCount>0)
            {
                List<Exception> errs = new List<Exception>();
                int errCount = Table.Verify(errs);
                if (errCount > 0)
                {
                    StringBuilder sb = new StringBuilder("Errors:\n\n");
                    int i = 0;
                    foreach (Exception e in errs)
                    {
                        sb.AppendFormat("{0}. {1}\n", i++, e.Message);
                    }

                    MessageBox.Show(sb.ToString());
                }
                Table.Commit();
            }
        }
        
        void ShowAll_Checked_Changed(object sender, EventArgs e)
        {
            this.GameOptions.IsShowAllCards = !this.GameOptions.IsShowAllCards;
        }

        void Reset_Clicked(object sender, EventArgs e)
        {
            if (!IsRunning())
                Reset();
        }

        void Pause_Clicked(object sender, EventArgs e)
        {
            if(IsRunning())
                Pause();
        }



        void Continue_Clicked(object sender, EventArgs e)
        {
            if (CanContinue())
                Continue();
        }

        void Next_Clicked(object sender, EventArgs e)
        {
            if (CanContinue())
                Next();
        }


        bool CanContinue()
        {
            return !State.IsFinalStage() && !IsRunning() && !IsManualStep() && CommitCount==0;
        }

        public bool IsManualStep()
        {
            return null!=State.Players.Current && State.Players.Current.IsManual && State.Players.Current.IsActive && State.Players.Current.IsActionReady;
        }

        /// <summary>
        /// Deprecate
        /// </summary>
        public string ActionMessage
        {
            get {
                if(IsManualStep())
                    return string.Format("{0} wants {1}?", State.Players.Current, State.Players.Current.BrainAction);

                return "Guys playing...";
            }
        }

        public bool IsRunning()
        {
            return _isRunning;
        }

        void UpdateToolStrips()
        {
            ToolStrips.EnableItem("...Next", CanContinue());
            ToolStrips.EnableItem("...Continue", CanContinue());
            ToolStrips.EnableItem("...Reset", !IsRunning());
            ToolStrips.EnableItem("...Pause", IsRunning());
            ToolStrips.EnableItem("...RunCount", !IsRunning());
            //ToolStrips.EnableItem("tsbShowAll", !IsRunning());
            ToolStrips.EnableItem("...RunCount",!IsRunning());
            ToolStrips.CheckItem("...ShowAll",GameOptions.IsShowAllCards);
            ToolStrips.EnableItem("...Commit", CommitCount > 0);
        }

        

        public void Next()
        {
            _stepMode = true;
            _isRunning = true;
            Server.NextStep();
            _isRunning = false;
            UpdateAll();
        }

        public int RunCount
        {
            get
            {
                if(null==_cbRunCount)
                    return 0;
                try
                {
                    int count = int.Parse(_cbRunCount.Text);
                    return count;
                }catch(Exception e)
                {
                    return 0;
                }
            }
            set
            {
                if (null == _cbRunCount)
                    return;
                _cbRunCount.Text = value.ToString();
                DebugStrip.Refresh();
            }
        }

        public void Continue()
        {
            _stepMode = false;
            _isRunning = true;
            _startTime = DateTime.Now.Ticks;

            bool needReset = false;
            _runCount = RunCount;
            
            UpdateToolStrips();
            DebugStrip.Refresh();

            do
            {
                HoldemAction.CurrentRunIndex++;

                GiveEvents();

                if(needReset)
                {
                    Reset();
                    needReset=false;
                }

                while ( _isRunning && Server.NextStep())
                {
                    if (Server.IsWantBreak)
                        _isRunning = false;

                    GiveEvents();
                }
                
                _runCount--;
                needReset = true;
                


            } while (_runCount > 0 && _isRunning);
            
            RunCount = _runCount;

            _isRunning = false;
            UpdateAll();
        }

        public void GiveEvents()
        {
            if ((DateTime.Now.Ticks - _startTime) > TimeSpan.FromMilliseconds(2000).Ticks)
            {
                _startTime = DateTime.Now.Ticks;
                RunCount = _runCount;
                Application.DoEvents();
            }
        }

        public void Pause()
        {
            _isRunning = false;
        }

        public void Reset()
        {
            Server.SwitchFirst();
            Server.NewGame();
        }

        protected override void StateChanged()
        {
            if(null!=State)
            {
                if(null!=_table)
                    _table.Game = State;

                State.GameStateChanged += new EventHandler(Game_StateChanged);
            }
        }

        void Game_StateChanged(object sender, EventArgs e)
        {
            _gameDirty = true;
        }



        public void ReplacePlayer(PlayerPanel panel, GamePlayer player)
        {
            GamePlayer curr = panel.Player;

            if (State.Players.IndexOf(player) >= 0)
            {
                // at the table -> swap
                State.Players.Swap(curr, player);
            }
            else
                State.Players.PlaceBefore(curr, player);
        }

        public IEnumerable<GamePlayer> GetAvailablePlayers(PlayerPanel panel)
        {
            foreach (GamePlayer p in State.Players)
                yield return p;
            if (null != State.Room)
                foreach (GamePlayer p in State.Room.Players)
                    yield return p;
        }
    }
}